///////////////////////////////////////////////////////////////////////////////
// File:        Player.cc
// Author:      671643387@qq.com
// Description: 
///////////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <memory.h>
#include <boost/bind.hpp>
#include <boost/thread/lock_guard.hpp>
#include "Log.hpp"
#include "player/PlayerManager.hpp"
#include "player/Player.hpp"

#if defined(__WINDOWS__)
#pragma warning(disable:4996)
#endif

///////////////////////////////////////////////////////////////////////////////
Player::Player(void)
    : id_(-1)
    , sock_()
    , owner_(NULL)
    , nstate_(NORMAL)
    , sendpending_(false)
    , mqlock_()
{

}

Player::~Player(void)
{

}

///////////////////////////////////////////////////////////////////////////////
int Player::GetID(void) const
{
    return id_;
}

std::string Player::GetRemoteIP(void) const
{
    return remoteip_;
}

void Player::Close(void)
{
    assert(owner_);
    DEBUG_LOG("close player connection %d", id_);
    _Close();
    owner_->ClosePlayer(id_);
}

///////////////////////////////////////////////////////////////////////////////
void Player::OnRead(void)
{

}

void Player::OnClose(void)
{

}

///////////////////////////////////////////////////////////////////////////////
std::size_t Player::RecvLen(void) const
{
    return recvcache_.size();
}

std::size_t Player::Recv(char* buf, std::size_t len)
{
    return recvcache_.read(buf, len);
}

std::size_t Player::Peek(char* buf, std::size_t len)
{
    assert(buf && len > 0);
    return recvcache_.peek(buf, len);
}

std::size_t Player::Skip(std::size_t len)
{
    return recvcache_.skip(len);
}

void Player::Send(const char* buf, std::size_t len)
{
    MessageBlockPtr mb(new MessageBlock(buf, len));
    {
        boost::lock_guard<boost::mutex> guard(mqlock_);
        messagequeue_.push_back(mb);
    }
    _PostSend();
}

///////////////////////////////////////////////////////////////////////////////
void Player::_Init(int id, socket_ptr sock, PlayerManager* mgr)
{
    this->id_ = id;
    this->sock_ = sock;
    this->owner_ = mgr;

    this->remoteip_ = sock->remote_endpoint().address().to_string();

#if defined(__DEBUG__)
    DEBUG_LOG("new player from %s:%d", 
        sock->remote_endpoint().address().to_string().c_str(),
        sock->remote_endpoint().port());
#endif
}

void Player::_Close(void)
{
    nstate_ = CLOSED;
    sock_->cancel();
    {
        boost::lock_guard<boost::mutex> guard(mqlock_);
        messagequeue_.clear();
    }
    OnClose();
    sock_->close();
}

void Player::_PostRecv(void)
{
    assert(sock_);
    if (nstate_ != NORMAL)
        return;

    sock_->async_receive(
        boost::asio::buffer(recvbuf_, std::min(sizeof(recvbuf_), recvcache_.space())),
        boost::bind(&Player::_OnRecv, this, _1, _2)
        );
}

void Player::_PostSend(void)
{
    assert(sock_);
    if (sendpending_ || messagequeue_.empty())
        return;

    sendpending_ = true;

    {
        boost::lock_guard<boost::mutex> guard(mqlock_);
        MessageBlockPtr& mb = messagequeue_.front();

        DEBUG_LOG("send %u bytes", mb->size());
        sock_->async_send(
            boost::asio::buffer(mb->contents(), mb->size()),
            boost::bind(&Player::_OnSend, this, _1, _2)
            );
    }
}

void Player::_OnRecv(const boost::system::error_code& err, 
    std::size_t bytes_transferred)
{
    if (nstate_ != NORMAL)
        return;

    if (err)
    {
        ERROR_LOG("%d, %s:%d, %d, %s", id_, 
            sock_->remote_endpoint().address().to_string().c_str(),
            sock_->remote_endpoint().port(),
            err.value(), err.message().c_str());
        Close();
        return;
    }

    assert(recvcache_.write(recvbuf_, bytes_transferred) == bytes_transferred);
    OnRead();
    _PostRecv();
}

void Player::_OnSend(const boost::system::error_code& err, 
    std::size_t bytes_transferred)
{
    assert(sendpending_);
    if (nstate_ != NORMAL)
        return;

    if (err)
    {
        ERROR_LOG("%d, %s:%d, recv", id_, 
            sock_->remote_endpoint().address().to_string().c_str(),
            sock_->remote_endpoint().port());
        Close();
        return;
    }

    DEBUG_LOG("real send %u bytes", bytes_transferred);

    {
        boost::lock_guard<boost::mutex> guard(mqlock_);
        MessageBlockPtr& mb = messagequeue_.front();

        if (bytes_transferred == mb->size())
            messagequeue_.pop_front();
        else
            mb->rpos(bytes_transferred);
    }

    sendpending_ = false;
    _PostSend();
}